﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ManoMachine.Assembler.Exceptions;
using ManoMachine.Assembler.Mnemonics;
using ManoMachine.Assembler.Mnemonics.Descriptors;

namespace ManoMachine.Assembler.Parsers
{
    internal static class AssemblyLineParser
    {
        internal static void Parse(ref AssemblyLine assemblyLine)
        {
            if (string.IsNullOrWhiteSpace(assemblyLine.Code))
            {
                return;
            }

            ///////////////////////////////////////////////////////////////////////////////////////////
            // Comment
            ///////////////////////////////////////////////////////////////////////////////////////////
            int indexOfComment = assemblyLine.Code.IndexOf('/');
            if (indexOfComment >= 0)
            {
                int lengthOfComment = assemblyLine.Code.Length - indexOfComment;
                assemblyLine.Comment = assemblyLine.Code.Substring(indexOfComment, lengthOfComment);
            }


            ///////////////////////////////////////////////////////////////////////////////////////////
            // Get the rest of line
            ///////////////////////////////////////////////////////////////////////////////////////////
            if (indexOfComment == 0)
            {
                return;
            }
            
            int lengthOfRest = (indexOfComment==-1)?assemblyLine.Code.Length : indexOfComment;
            string restOfLine = assemblyLine.Code.Substring(0, lengthOfRest).Trim();

            ///////////////////////////////////////////////////////////////////////////////////////////
            // spilt the label and the instruction
            ///////////////////////////////////////////////////////////////////////////////////////////
            string labelPart = null;
            string instructionPart = null;
            string[] strings = restOfLine.Split(',');
            if (strings.Length > 2)
            {
                throw new AssemblerException(string.Format("more than comma is detected"), assemblyLine);
            }
            if (strings.Length == 2)
            {
                labelPart = strings[0];
                instructionPart = strings[1];
            }
            else if (strings.Length == 1)
            {
                instructionPart = strings[0];
            }
            else
            {
                throw new AssemblerException(string.Format("unexpected format is detected"), assemblyLine);
            }

            ///////////////////////////////////////////////////////////////////////////////////////////
            // Validate the label part
            ///////////////////////////////////////////////////////////////////////////////////////////
            if (labelPart != null)
            {
                labelPart = labelPart.Trim();
                MatchCollection matchCollection = Regex.Matches(labelPart, "[a-zA-Z][a-zA-Z0-9]?[a-zA-Z0-9]?");
                if (matchCollection.Count == 1 && matchCollection[0].Index == 0 && matchCollection[0].Length == labelPart.Length)
                {
                    assemblyLine.Label = labelPart;
                }
                else
                {
                    throw new AssemblerException("invalid label", assemblyLine);
                }
            }

            ///////////////////////////////////////////////////////////////////////////////////////////
            // Instruction Part
            ///////////////////////////////////////////////////////////////////////////////////////////
            instructionPart = instructionPart.Trim();
            if (labelPart!=null && string.IsNullOrWhiteSpace(instructionPart))
            {
                throw new AssemblerException("invalid instruction", assemblyLine);
            }
            if (string.IsNullOrWhiteSpace(instructionPart))
            {
                return;
            }

            assemblyLine.InstructionParts = instructionPart.Split(new [] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
            assemblyLine.MnemonicDescriptor = MnemonicsDictionary.GetDescriptor(assemblyLine.InstructionParts[0]);
            if (assemblyLine.MnemonicDescriptor == null)
            {
                throw new AssemblerException("invalid mnemonic", assemblyLine);
            }
            //if (labelPart != null )
            //{
            //    if (!(assemblyLine.MnemonicDescriptor is PSIDescriptor) || string.Compare(assemblyLine.InstructionParts[0],"END",true)==0)
            //    {
            //        throw new AssemblerException("invalid labeling", assemblyLine);                    
            //    }
            //}

            if (!assemblyLine.MnemonicDescriptor.IsValidInstruction(assemblyLine))
            {
                throw new AssemblerException("invalid instruction", assemblyLine);
            }
        }
    }
}
